From b69f92f3012eb078af2e6b48fc9c2dbed99c2742 Mon Sep 17 00:00:00 2001 From: "sos22@donkeykong.cl.cam.ac.uk" Date: Wed, 28 Jul 2004 15:08:31 +0000 Subject: [PATCH] bitkeeper revision 1.1098.1.1 (4107c16f_jhuAGUiMBcotuiFhO6dHQ) Add a simple debugger for Xen and domain 0. This isn't anything like as complete as PDB; it can't access any userspace other than the current process's and it can't make any changes to a running process. On the other hand, it's probably slightly less fragile. The code's a lot cleaner, as well. --- .rootkeys | 2 + BitKeeper/etc/logging_ok | 1 + xen/arch/x86/x86_32/xdb_trap.S | 35 +++ xen/arch/x86/xdb.c | 378 +++++++++++++++++++++++++++++++++ xen/common/domain.c | 4 + xen/common/kernel.c | 12 ++ xen/common/keyhandler.c | 6 + xen/drivers/char/console.c | 6 +- xen/drivers/char/serial.c | 41 ++-- 9 files changed, 468 insertions(+), 17 deletions(-) create mode 100644 xen/arch/x86/x86_32/xdb_trap.S create mode 100644 xen/arch/x86/xdb.c diff --git a/.rootkeys b/.rootkeys index b5b50680da..43e1f1dfe4 100644 --- a/.rootkeys +++ b/.rootkeys @@ -449,10 +449,12 @@ 3ddb79bcecupHj56ZbTa3B0FxDowMg xen/arch/x86/x86_32/entry.S 3ddb79bcHwuCQDjBICDTSis52hWguw xen/arch/x86/x86_32/mm.c 3ddb79bc4nTpGQOe6_-MbyZzkhlhFQ xen/arch/x86/x86_32/usercopy.c +4107c15e_NqNYew2EXroXz2mgTAMWQ xen/arch/x86/x86_32/xdb_trap.S 3ddb79bcOMCu9-5mKpjIh5d0qqBDPg xen/arch/x86/x86_32/xen.lds 40e96d3aLDI-nViMuYneD7VKYlZrVg xen/arch/x86/x86_64/entry.S 40e96d3ahBTZqbTViInnq0lM03vs7A xen/arch/x86/x86_64/usercopy.c 40e96d3akN3Hu_J5Bk-WXD8OGscrYQ xen/arch/x86/x86_64/xen.lds +4107c15e-VmEcLsE-7JCXZaabI8C7A xen/arch/x86/xdb.c 3ddb79bdff-gj-jFGKjOejeHLqL8Lg xen/common/Makefile 3e397e66AyyD5fYraAySWuwi9uqSXg xen/common/ac_timer.c 4022a73c_BbDFd2YJ_NQYVvKX5Oz7w xen/common/debug-linux.c diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 34d574e882..36759e9962 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -38,6 +38,7 @@ smh22@boulderdash.cl.cam.ac.uk smh22@labyrinth.cl.cam.ac.uk smh22@tempest.cl.cam.ac.uk smh22@uridium.cl.cam.ac.uk +sos22@donkeykong.cl.cam.ac.uk sos22@labyrinth.cl.cam.ac.uk tlh20@elite.cl.cam.ac.uk tlh20@labyrinth.cl.cam.ac.uk diff --git a/xen/arch/x86/x86_32/xdb_trap.S b/xen/arch/x86/x86_32/xdb_trap.S new file mode 100644 index 0000000000..262c6dbf73 --- /dev/null +++ b/xen/arch/x86/x86_32/xdb_trap.S @@ -0,0 +1,35 @@ +.global trap_to_xendbg +.extern __trap_to_xendbg + +#define SAVE_ALL_NOSEGREGS \ + pushw $0; \ + pushw %gs; \ + pushw $0; \ + pushw %fs; \ + pushw $0; \ + pushw %es; \ + pushw $0; \ + pushw %ds; \ + pushl %eax; \ + pushl %ebp; \ + pushl %edi; \ + pushl %esi; \ + pushl %edx; \ + pushl %ecx; \ + pushl %ebx; + + // Save the register state and call __trap_to_xendbg +trap_to_xendbg: + pushw $0 + pushw %ss + pushl %esp //We'll fix this up later, in __trap_to_xendbg, by adding 8 + pushf + pushw $0 + pushw %cs + pushl 16(%esp) +1: pushl $0 // Orig_eax + SAVE_ALL_NOSEGREGS + pushl %esp + call __trap_to_xendbg + add $72, %esp + ret diff --git a/xen/arch/x86/xdb.c b/xen/arch/x86/xdb.c new file mode 100644 index 0000000000..8548953108 --- /dev/null +++ b/xen/arch/x86/xdb.c @@ -0,0 +1,378 @@ +/* Simple hacked-up version of pdb for us in post-mortem debugging of + Xen and domain 0. This should be a little cleaner, hopefully. Note + that we can't share a serial line with PDB. */ +#include +#include +#include +#include +#include + +/* Printk isn't particularly safe just after we've trapped to the + debugger. so avoid it. */ +#define dbg_printk(...) + +static int +xendbg_serhnd = -1; + +static void +xendbg_put_char(u8 data) +{ + serial_putc(xendbg_serhnd, data); +} + +static u8 +xendbg_get_char(void) +{ + u8 ch; + extern unsigned char __serial_getc(int handle); + ch = __serial_getc(xendbg_serhnd); + return ch; +} + +static int +hex_char_val(unsigned char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else + BUG(); +} + +static unsigned char +val_to_hex_char(unsigned val) +{ + if (val < 10) + return val + '0'; + else if (val < 16) + return val - 10 + 'a'; + else + BUG(); +} + +/* Receive a command. Returns -1 on csum error, 0 otherwise. */ +/* Does not acknowledge. */ +static int +attempt_receive_packet(char *recv_buf) +{ + int count; + u8 csum; + u8 received_csum; + u8 ch; + + /* Skip over everything up to the first '$' */ + while ((ch = xendbg_get_char()) != '$') + ; + csum = 0; + for (count = 0; count < 4096; count++) { + ch = xendbg_get_char(); + if (ch == '#') + break; + recv_buf[count] = ch; + csum += ch; + } + if (count == 4096) { + dbg_printk("WARNING: GDB sent a stupidly big packet.\n"); + return -1; + } + recv_buf[count] = 0; + received_csum = hex_char_val(xendbg_get_char()) * 16 + + hex_char_val(xendbg_get_char()); + if (received_csum == csum) { + return 0; + } else { + return -1; + } +} + +/* Send a string of bytes to the debugger. */ +static void +xendbg_send(const char *buf, int count) +{ + int x; + for (x = 0; x < count; x++) + xendbg_put_char(buf[x]); +} + +/* Receive a command, discarding up to ten packets with csum + * errors. Acknowledges all received packets. */ +static int +receive_command(char *recv_buf) +{ + int r; + int count; + + count = 0; + do { + r = attempt_receive_packet(recv_buf); + if (r < 0) + xendbg_send("-", 1); + else + xendbg_send("+", 1); + count++; + } while (r < 0 && count < 10); + return r; +} + +static void +u32_to_hex_u8(unsigned char val, char *buf) +{ + if (val >= 256) + BUG(); + sprintf(buf, "%.02x\n", val); +} + +static void +u32_to_hex_u32(unsigned val, char *buf) +{ + sprintf(buf, "%.08x\n", val); +} + +static void +xendbg_send_hex_u8(unsigned char val) +{ + char buf[2]; + u32_to_hex_u8(val, buf); + xendbg_send(buf, 2); +} + +static u8 +xendbg_reply_csum; + +static void +xendbg_start_reply(void) +{ + xendbg_reply_csum = 0; + xendbg_send("$", 1); +} + +static void +xendbg_sendrep_data(const unsigned char *data, unsigned long len) +{ + int x; + + for (x = 0; x < len; x++) { + xendbg_put_char(data[x]); + xendbg_reply_csum += data[x]; + } +} + +/* Return 0 if the reply was successfully received, !0 otherwise. */ +static int +xendbg_finish_reply(void) +{ + char ch; + + xendbg_send("#", 1); + xendbg_send_hex_u8(xendbg_reply_csum); + ch = xendbg_get_char(); + if (ch == '+') + return 0; + else + return 1; +} + +static void +xendbg_sendrep_hex_u8(unsigned val) +{ + char buf[2]; + u32_to_hex_u8(val, buf); + xendbg_sendrep_data(buf, 2); +} + +static void +xendbg_sendrep_hex_u32(unsigned val) +{ + char buf[8]; + u32_to_hex_u32(val, buf); + xendbg_sendrep_data(buf, 8); +} + +static void +xendbg_sendrep_hex_u32_le(unsigned val) +{ + val = (((val >> 0) & 0xff) << 24) | + (((val >> 8) & 0xff) << 16) | + (((val >> 16) & 0xff) << 8) | + (((val >> 24) & 0xff) << 0); + xendbg_sendrep_hex_u32(val); +} + +static int +handle_memory_read_command(unsigned long addr, unsigned long length) +{ + int x; + unsigned char val; + int r; + unsigned old_s_limit; + + dbg_printk("Memory read starting at %lx, length %lx.\n", addr, + length); + old_s_limit = current->addr_limit.seg; + current->addr_limit.seg = ~0; + xendbg_start_reply(); + for (x = 0; x < length; x++) { + r = copy_from_user(&val, (void *)(addr + x), 1); + if (r != 0) { + dbg_printk("Error reading from %lx.\n", addr + x); + break; + } + xendbg_sendrep_hex_u8(val); + } + if (x == 0) + xendbg_sendrep_data("E05", 3); + dbg_printk("Read done.\n"); + current->addr_limit.seg = old_s_limit; + return xendbg_finish_reply(); +} + +static int +xendbg_send_reply(const char *buf) +{ + xendbg_start_reply(); + xendbg_sendrep_data(buf, strlen(buf)); + return xendbg_finish_reply(); +} + +static int +handle_register_read_command(struct pt_regs *regs) +{ + xendbg_start_reply(); + xendbg_sendrep_hex_u32_le(regs->eax); + xendbg_sendrep_hex_u32_le(regs->ecx); + xendbg_sendrep_hex_u32_le(regs->edx); + xendbg_sendrep_hex_u32_le(regs->ebx); + xendbg_sendrep_hex_u32_le(regs->esp); + xendbg_sendrep_hex_u32_le(regs->ebp); + xendbg_sendrep_hex_u32_le(regs->esi); + xendbg_sendrep_hex_u32_le(regs->edi); + xendbg_sendrep_hex_u32_le(regs->eip); + xendbg_sendrep_hex_u32_le(regs->eflags); + xendbg_sendrep_hex_u32_le(regs->xcs); + xendbg_sendrep_hex_u32_le(regs->xss); + xendbg_sendrep_hex_u32_le(regs->xes); + xendbg_sendrep_hex_u32_le(regs->xfs); + xendbg_sendrep_hex_u32_le(regs->xgs); + return xendbg_finish_reply(); +} + +static unsigned long +hex_to_int(const char *start, const char **end) +{ + return simple_strtol(start, (char **)end, 16); +} + +static int +process_command(const char *received_packet, struct pt_regs *regs) +{ + const char *ptr; + unsigned long addr, length; + int retry; + int counter; + int resume = 0; + + /* Repeat until gdb acks the reply */ + counter = 0; + do { + switch (received_packet[0]) { + case 'g': /* Read registers */ + retry = handle_register_read_command(regs); + break; + case 'm': /* Read memory */ + addr = hex_to_int(received_packet + 1, &ptr); + if (ptr == received_packet + 1 || + ptr[0] != ',') { + xendbg_send_reply("E03"); + return 0; + } + length = hex_to_int(ptr + 1, &ptr); + if (ptr[0] != 0) { + xendbg_send_reply("E04"); + return 0; + } + retry = + handle_memory_read_command(addr, + length); + break; + case 'G': /* Write registers */ + case 'M': /* Write memory */ + retry = xendbg_send_reply("E02"); + break; + case 'D': + resume = 1; + retry = xendbg_send_reply(""); + break; + case 'c': /* Resume at current address */ + case 's': /* Single step */ + case '?': + retry = xendbg_send_reply("S01"); + break; + default: + retry = xendbg_send_reply(""); + break; + } + counter++; + } while (retry == 1 && counter < 10); + if (retry) { + dbg_printk("WARNING: gdb disappeared when we were trying to send it a reply.\n"); + return 1; + } + return resume; +} + +void +__trap_to_xendbg(struct pt_regs *regs) +{ + int resume = 0; + int r; + static int xendbg_running; + static char recv_buf[4096]; + + if (xendbg_serhnd < 0) { + dbg_printk("Debugger not ready yet.\n"); + return; + } + /* We rely on our caller to ensure we're only on one processor + * at a time... We should probably panic here, but given that + * we're a debugger we should probably be a little tolerant of + * things going wrong. */ + if (xendbg_running) { + dbg_printk("WARNING WARNING WARNING: Avoiding recursive xendbg.\n"); + return; + } + xendbg_running = 1; + /* trap_to_xendbg gets esp slightly wrong. Correct for this. */ + regs->esp += 8; + + dbg_printk("Waiting for GDB to attach to XenDBG\n"); + /* Urgg... hope this is right... */ + while (resume == 0) { + r = receive_command(recv_buf); + if (r < 0) { + dbg_printk("GDB disappeared, trying to resume Xen...\n"); + resume = 1; + } else + resume = process_command(recv_buf, regs); + } + xendbg_running = 0; +} + +void +initialize_xendbg(void) +{ + extern char opt_xendbg[]; + + if (!strcmp(opt_xendbg, "none")) + return; + xendbg_serhnd = parse_serial_handle(opt_xendbg); + if (xendbg_serhnd == -1) + panic("Can't parse %s as XDB serial info.\n", opt_xendbg); + + /* Acknowledge any spurious GDB packets. */ + xendbg_put_char('+'); + + printk("Xendbg initialised.\n"); +} diff --git a/xen/common/domain.c b/xen/common/domain.c index 7082f07bed..68c7c79914 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -163,6 +163,8 @@ void domain_crash(void) BUG(); } +extern void trap_to_xendbg(void); + void domain_shutdown(u8 reason) { struct domain *d; @@ -172,6 +174,8 @@ void domain_shutdown(u8 reason) extern void machine_restart(char *); extern void machine_halt(void); + trap_to_xendbg(); + if ( reason == 0 ) { printk("Domain 0 halted: Our work here is done.\n"); diff --git a/xen/common/kernel.c b/xen/common/kernel.c index 7facb69cac..0864c9fab7 100644 --- a/xen/common/kernel.c +++ b/xen/common/kernel.c @@ -64,6 +64,8 @@ int opt_ignorebiostables=0; int opt_watchdog=0; /* opt_pdb: Name of serial port for Xen pervasive debugger (and enable pdb) */ unsigned char opt_pdb[10] = "none"; +/* opt_pdb: Name of serial port for Xen debugger (and enable xendbg) */ +unsigned char opt_xendbg[10] = "none"; /* opt_tbuf_size: trace buffer size (in pages) */ unsigned int opt_tbuf_size = 10; /* opt_sched: scheduler - default to Borrowed Virtual Time */ @@ -98,6 +100,7 @@ static struct { { "ignorebiostables", OPT_BOOL, &opt_ignorebiostables }, { "watchdog", OPT_BOOL, &opt_watchdog }, { "pdb", OPT_STR, &opt_pdb }, + { "xendbg", OPT_STR, &opt_xendbg }, { "tbuf_size", OPT_UINT, &opt_tbuf_size }, { "sched", OPT_STR, &opt_sched }, { "physdev_dom0_hide", OPT_STR, &opt_physdev_dom0_hide }, @@ -108,6 +111,9 @@ static struct { }; +void initialize_xendbg(void); +void trap_to_xendbg(void); + void cmain(multiboot_info_t *mbi) { struct domain *new_dom; @@ -163,6 +169,8 @@ void cmain(multiboot_info_t *mbi) init_console(); + initialize_xendbg(); + /* HELLO WORLD --- start-of-day banner text. */ printk(XEN_BANNER); printk(" http://www.cl.cam.ac.uk/netos/xen\n"); @@ -291,6 +299,10 @@ void cmain(multiboot_info_t *mbi) shadow_mode_init(); + printk("Trapping to debugger.\n"); + trap_to_xendbg(); + printk("Trapped to debugger and came back.\n"); + /* * We're going to setup domain0 using the module(s) that we stashed safely * above our heap. The second module, if present, is an initrd ramdisk. diff --git a/xen/common/keyhandler.c b/xen/common/keyhandler.c index aff7ccfd1b..ad04f8476d 100644 --- a/xen/common/keyhandler.c +++ b/xen/common/keyhandler.c @@ -115,6 +115,11 @@ extern void perfc_reset(unsigned char key, void *dev_id, struct pt_regs *regs); #endif +void do_panic_key(unsigned char key, void *dev_id, struct pt_regs *regs) +{ + panic("Panic requested from console"); +} + void initialize_keytable(void) { add_key_handler('d', dump_registers, "dump registers"); @@ -128,4 +133,5 @@ void initialize_keytable(void) add_key_handler('p', perfc_printall, "print performance counters"); add_key_handler('P', perfc_reset, "reset performance counters"); #endif + add_key_handler('%', do_panic_key, "Panic Xen"); } diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c index b1887fa1fb..7008bef76e 100644 --- a/xen/drivers/char/console.c +++ b/xen/drivers/char/console.c @@ -440,6 +440,8 @@ void console_endboot(int disable_vga) * ************************************************************** */ +extern void trap_to_xendbg(void); + void panic(const char *fmt, ...) { va_list args; @@ -450,7 +452,9 @@ void panic(const char *fmt, ...) va_start(args, fmt); (void)vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); - + + trap_to_xendbg(); + /* Spit out multiline message in one go. */ spin_lock_irqsave(&console_lock, flags); __putstr("\n****************************************\n"); diff --git a/xen/drivers/char/serial.c b/xen/drivers/char/serial.c index e6ae6c60b9..7c710cbada 100644 --- a/xen/drivers/char/serial.c +++ b/xen/drivers/char/serial.c @@ -420,29 +420,17 @@ static int byte_matches(int handle, unsigned char *pc) return 0; } -unsigned char serial_getc(int handle) +unsigned char __serial_getc(int handle) { uart_t *uart = &com[handle & SERHND_IDX]; unsigned char c; - unsigned long flags; - spin_lock_irqsave(&uart->lock, flags); - - while ( uart->rxbufp != uart->rxbufc ) - { - c = uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufc++)]; - if ( byte_matches(handle, &c) ) - goto out; - } - - disable_irq(uart->irq); - /* disable_irq() may have raced execution of uart_rx(). */ while ( uart->rxbufp != uart->rxbufc ) { c = uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufc++)]; if ( byte_matches(handle, &c) ) - goto enable_and_out; + return c; } /* We now wait for the UART to receive a suitable character. */ @@ -452,8 +440,29 @@ unsigned char serial_getc(int handle) c = inb(uart->io_base + RBR); } while ( !byte_matches(handle, &c) ); - - enable_and_out: + + return c; +} + +unsigned char serial_getc(int handle) +{ + uart_t *uart = &com[handle & SERHND_IDX]; + unsigned char c; + unsigned long flags; + + spin_lock_irqsave(&uart->lock, flags); + + while ( uart->rxbufp != uart->rxbufc ) + { + c = uart->rxbuf[MASK_RXBUF_IDX(uart->rxbufc++)]; + if ( byte_matches(handle, &c) ) + goto out; + } + + disable_irq(uart->irq); + + c = __serial_getc(handle); + enable_irq(uart->irq); out: spin_unlock_irqrestore(&uart->lock, flags); -- 2.30.2